(◕‿◕✿) Beautiful Commits: My Way

您所在的位置:网站首页 git merge (◕‿◕✿) Beautiful Commits: My Way

(◕‿◕✿) Beautiful Commits: My Way

2023-01-13 16:05| 来源: 网络整理| 查看: 265

In this article, I explain my philosophy on committing code and then provide a step-by-step guide to editing code commits with the git rebase command to prepare for a pull request and merge to main branch.

Git rebase is a great tool for managing your code commits and being intentional and thoughtful about how the commit history ultimately looks on your code repository. Git rebase is not an intuitive series of steps to walk through for the first time, so I am posting the details of the commit strategy that I use to creating meaningful and beautiful commits in hopes that you can find something helpful to use as well~

Why Do Commits Matter?

Let's set the table with an example pull request:

No alt text provided for this image

Take a glance at the PR above - wear your reviewer hat and take a look. This isn't bad at all... but we can make it better :)

Let's talk about the commits. Is nine commits a bad thing? I don't know - maybe 🤷. Is nineteen commits a bad thing? What about ninety-nine? There certainly isn't a rule that dictates how many commits to limit a PR to, but I would venture to say that nine commits for +89 lines of code is excessive.

Also I'm lying: the rule is 5.

... not saying that all PRs will be 5 or less commits, but if your PRs are consistently greater than 5 commits then you have likely an opportunity to tighten it up.

This matters because sometimes it helps to reverse engineer or troubleshoot a codebase on a per-commit basis: Perhaps I'm looking for a specific change that introduced a bug, so I look at commit history • Perhaps I'm interested in what other changes were introduced along with a specific line of code, so I use the blame view to identify the commit and look at the other updated lines of code • Perhaps I'm resolving a merge conflict and I need context for a changed line of code, so I examine the commit • etc...

When the need to review past commits arises, mislabeled commits and fragmented changes will put the brakes on productivity while also introducing further confusion. An investment in authoring beautiful commits pays dividends in the same scenario; particularly when the person who is reviewing the code commits did not make the changes themselves.

To Squash or Not to Squash

Some teams successfully side-step the ugly commit problem by squashing all commits in a pull request before applying the changes to the main branch. Squash-and-merge can be an effective strategy that helps us avoid having to scrutinize the commit history of feature branches, but this comes at the cost of tightly coupling all code changes in a given pull request that may or may not be related (i.e. the ugly commit problem is replaced by the big commit problem).

the ugly commit problem is replaced by the big commit problem

I've worked in both camps, but my current professional preference is to not squash commits when applying them to main.

Make it Beautiful with Rebase: Step-by-Step

My solution is to work fast in my feature branch by making regular (and ugly) commits. I later beautify the commit history in my feature branch with git rebase just before opening a pull request.

While many engineers can navigate the git rebase command in their sleep, this can be a challenging task for those not used to it. When I started my career I was, very poorly, using SVN - and eventually started using Git to the point where I was comfortable with an add/commit/push workflow, but struggled performing any additional Git functions. After identifying that I wanted to start running cleaner commits, it took some practice and iteration to find what worked for me. Let's get into the formula →

During Development • Sequence the ChangesNo alt text provided for this image

During development, I commit to my feature branch all the time. I follow the "commit early, commit often" mantra with reckless abandon.

There was a time when I was a much more intentional about trying to identify that perfect moment.. that perfect state of the code that is ready to preserve for eternity as a commit -- but this is ultimately an exercise in futility. In the development branch edits are going to happen; and because I am unsure how I will further refine this piece of code or how I will want to represent the completed code changes, I treat development commits the same way I treat the source code: as mutable pieces of in-progress work.

Instead of slowing down and breaking flow to think about the ideal snapshot while I'm authoring changes, my strategy to is commit on-demand, anytime it feels good, and to later edit the commit history to perfection in post-production.

With this mindset, if I were to re-write the changes from the above example PR, one thing I would do differently is to add a sequence number to every commit I make.. yup, git commit -m "1 - ..." and "2 - ...", and "3 - ..." and so on.

Adding a sequence number to every commit message does three things for me:

It is a visual cue that reminds me that the current branch I am working out of is not ready for PR yet.It keeps me cognizant of how many commits I have made to the current branch and by some approximation gives me an idea of how big the overall change is. This helps me be mindful about keeping code changes to a reasonable size (5,000 line review anyone?).It is an easy reminder of how many commits to rebase when I am ready to prepare for a PR.

Let's keep diving on that last point...

While I am developing code and making commits, I'm often checking the last few lines of `git log` while I'm working. If I have been adding indices to my commit messages, the git log for the completed changes will look like this:

No alt text provided for this image

You may be thinking that manually indexing commits is silly when you can just run `git rev-list --count --no-merges main..` and it is, but I'm never going to remember that command on the fly, so I'll stick to my blockhead method and retain the ancillary benefits of 1 & 2 above.

At Dev Complete • Rebase the Changes

Once I have completed development on the functionality that I am working on, I will loop back to review and edit the commits in my feature branch. When I am ready to rebase, I just check git log to see how many commits I have made to the feature branch, and I run:

git rebase -i HEAD~

No alt text provided for this image

That gets us to this eye-full:

No alt text provided for this image

For a long time this screen was a non-starter for me, but again my dummy method of adding a sequence number provides clarity; these are my commits top-down, earliest to latest.

To make this beautiful, I'm going to ask myself: How can I condense these changes to the smallest number of commits with messages that succinctly tell the story of the updates that I am making to the codebase?

In this case, I have three distinct pieces of change that I want to capture:

Commits 1, 2, 3, and 4 are just me adding core code changes. These are sufficiently similar such that they can be squashed into a single commit that adds an atomic piece of functionality that may be worth reviewing later. Of course, I'll want to reword the commit message to better define the aggregated commits.Commits 5, 6, and 7 are additional changes that I would describe as house-keeping tasks that facilitate quality in my repo, but are sufficiently different from my first commit to the point that I'm okay squashing them into a second commit - reworded for accuracy of course.Commits 8 and 9 are a code refactor that I may consider re-ordering above 5, 6, and 7 and squashing into the first commit as well - but let's say in this case keeping it separate tells the story of how existing functionality was updated and I want to retain that in my repo. My commit message on 8 isn't that bad, so here I will pick commit 8, and squash my formatting from 9 into it.

Using the git CLI, I am going to tell this same story to the interactive rebase by updating the commands next to each commit:

No alt text provided for this image

During the execution of git rebase, it prompts me to make these updates via VIM editor, so I'll do a little :wq! action to save my changes.

The first screen will ask me to reword the first commit. Because it's an indexed commit message it's easy for me to determine how I should edit the message - this is followed by a screen that allows me to retain the squashed commits as part of a multi-line commit message. Because I'm word-smithing the first commit message to something appropriate for the group of changes I discard the additional lines of messages:

No alt text provided for this image

When moving onto the second reworded commit and squashes, I will update commit message 5 to something more appropriate and then also discard the additional lines of squashed commit messages:

No alt text provided for this image

In the prompts for the last squash, I forgot to indicate that I wanted to reword commit 8 to remove the sequence number 😬 .. but this is actually a forgiving screen to edit commit messages and I can do it anyway:

No alt text provided for this image

After the rebase is complete, Git provides a somewhat verbose summary of the changes. I never read this except for the last line - but the changes are nicely detailed:

No alt text provided for this image

After performing the rebase, git log shows that we have successfully edited the commit history and now my changes look ready to ship:

No alt text provided for this image

Time to push the changes to remote and open a PR.

Keep in mind: if you have already pushed your feature branch to remote at any time during development then your local and remote will be out of sync because of locally editing the commit history with rebase. Git push will reject the push and provide error output.

This is no biggie if we understand why it happened. A force-push will sync remote with the local state of the branch:

No alt text provided for this image

Finally, this PR is ready to open and review with a series of beautiful commits 🎉

No alt text provided for this imageFinal Thoughts

This is the method that I have been able to integrate into my development flow to keep track of where I am, ensure that I am pushing beautiful commits to the main branch, and opening attractive PRs. This method has been especially helpful in cases where I have repeatedly pushed commits to pass a CI pipeline as it can clobber a long list of red X's on the front page of the PR.

Git rebase can feel clunky if you do not use it often, but I highly recommend putting effort into this learning utility and reaching for it to help you manage your commits~



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3